// ============================================================================
//
// LCP-net framework by Pierre CHATEL chatelp@gmail.com
// Distributed under GPLv3
// Copyright (C) THALES Land & Joint Systems, 2008, 2009. All rights reserved
//
// ============================================================================

/**
 * <copyright>
 * </copyright>
 *
 * $Id$
 */
package com.thalesgroup.preferenceFramework.models.LCPnet.impl;

import com.thalesgroup.preferenceFramework.models.LCPnet.CIArc;
import com.thalesgroup.preferenceFramework.models.LCPnet.CIT;
import com.thalesgroup.preferenceFramework.models.LCPnet.CITLine;
import com.thalesgroup.preferenceFramework.models.LCPnet.LCPnetPackage;
import com.thalesgroup.preferenceFramework.models.LCPnet.LNodeValue;
import com.thalesgroup.preferenceFramework.models.LCPnet.exceptions.LCPnetEvaluationException;

import java.util.Collection;

import net.sourceforge.jFuzzyLogic.defuzzifier.DefuzzifierCenterOfGravity;
import net.sourceforge.jFuzzyLogic.membership.MembershipFunction;
import net.sourceforge.jFuzzyLogic.membership.MembershipFunctionPieceWiseLinear;
import net.sourceforge.jFuzzyLogic.rule.FuzzyRule;
import net.sourceforge.jFuzzyLogic.rule.FuzzyRuleExpression;
import net.sourceforge.jFuzzyLogic.rule.FuzzyRuleSet;
import net.sourceforge.jFuzzyLogic.rule.FuzzyRuleTerm;
import net.sourceforge.jFuzzyLogic.rule.LinguisticTerm;
import net.sourceforge.jFuzzyLogic.rule.Variable;
import net.sourceforge.jFuzzyLogic.ruleAggregation.RuleAggregationMethodMax;
import net.sourceforge.jFuzzyLogic.ruleConnection.RuleConnectionMethod;
import net.sourceforge.jFuzzyLogic.ruleConnection.RuleConnectionMethodAndMin;
import net.sourceforge.jFuzzyLogic.ruleImplication.RuleImplicationMethod;
import net.sourceforge.jFuzzyLogic.ruleImplication.RuleImplicationMethodMin;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;

import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.EList;

import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;

import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;

import org.eclipse.emf.ecore.util.EObjectContainmentWithInverseEList;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.util.InternalEList;

/**
 * <!-- begin-user-doc --> An implementation of the model object '
 * <em><b>CIT</b></em>'. <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link com.thalesgroup.preferenceFramework.models.LCPnet.impl.CITImpl#getName <em>Name</em>}</li>
 *   <li>{@link com.thalesgroup.preferenceFramework.models.LCPnet.impl.CITImpl#getArc <em>Arc</em>}</li>
 *   <li>{@link com.thalesgroup.preferenceFramework.models.LCPnet.impl.CITImpl#getLines <em>Lines</em>}</li>
 * </ul>
 * </p>
 *
 * @generated
 */
public class CITImpl extends EObjectImpl implements CIT {
	/**
	 * The default value of the '{@link #getName() <em>Name</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getName()
	 * @generated
	 * @ordered
	 */
	protected static final String NAME_EDEFAULT = null;
	/**
	 * The cached value of the '{@link #getName() <em>Name</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getName()
	 * @generated
	 * @ordered
	 */
	protected String name = NAME_EDEFAULT;
	/**
	 * The cached value of the '{@link #getLines() <em>Lines</em>}' containment reference list.
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @see #getLines()
	 * @generated
	 * @ordered
	 */
	protected EList<CITLine> lines;

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	protected CITImpl() {
		super();
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return LCPnetPackage.Literals.CIT;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public String getName() {
		return name;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setName(String newName) {
		String oldName = name;
		name = newName;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LCPnetPackage.CIT__NAME, oldName, name));
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public CIArc getArc() {
		if (eContainerFeatureID != LCPnetPackage.CIT__ARC) return null;
		return (CIArc)eContainer();
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public NotificationChain basicSetArc(CIArc newArc, NotificationChain msgs) {
		msgs = eBasicSetContainer((InternalEObject)newArc, LCPnetPackage.CIT__ARC, msgs);
		return msgs;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public void setArc(CIArc newArc) {
		if (newArc != eInternalContainer() || (eContainerFeatureID != LCPnetPackage.CIT__ARC && newArc != null)) {
			if (EcoreUtil.isAncestor(this, newArc))
				throw new IllegalArgumentException("Recursive containment not allowed for " + toString());
			NotificationChain msgs = null;
			if (eInternalContainer() != null)
				msgs = eBasicRemoveFromContainer(msgs);
			if (newArc != null)
				msgs = ((InternalEObject)newArc).eInverseAdd(this, LCPnetPackage.CI_ARC__IMPORTANCE_TABLE, CIArc.class, msgs);
			msgs = basicSetArc(newArc, msgs);
			if (msgs != null) msgs.dispatch();
		}
		else if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, LCPnetPackage.CIT__ARC, newArc, newArc));
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	public EList<CITLine> getLines() {
		if (lines == null) {
			lines = new EObjectContainmentWithInverseEList<CITLine>(CITLine.class, this, LCPnetPackage.CIT__LINES, LCPnetPackage.CIT_LINE__TABLE);
		}
		return lines;
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@SuppressWarnings("unchecked")
	@Override
	public NotificationChain eInverseAdd(InternalEObject otherEnd,
			int featureID, NotificationChain msgs) {
		switch (featureID) {
			case LCPnetPackage.CIT__ARC:
				if (eInternalContainer() != null)
					msgs = eBasicRemoveFromContainer(msgs);
				return basicSetArc((CIArc)otherEnd, msgs);
			case LCPnetPackage.CIT__LINES:
				return ((InternalEList<InternalEObject>)(InternalEList<?>)getLines()).basicAdd(otherEnd, msgs);
		}
		return super.eInverseAdd(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd,
			int featureID, NotificationChain msgs) {
		switch (featureID) {
			case LCPnetPackage.CIT__ARC:
				return basicSetArc(null, msgs);
			case LCPnetPackage.CIT__LINES:
				return ((InternalEList<?>)getLines()).basicRemove(otherEnd, msgs);
		}
		return super.eInverseRemove(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eBasicRemoveFromContainerFeature(
			NotificationChain msgs) {
		switch (eContainerFeatureID) {
			case LCPnetPackage.CIT__ARC:
				return eInternalContainer().eInverseRemove(this, LCPnetPackage.CI_ARC__IMPORTANCE_TABLE, CIArc.class, msgs);
		}
		return super.eBasicRemoveFromContainerFeature(msgs);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case LCPnetPackage.CIT__NAME:
				return getName();
			case LCPnetPackage.CIT__ARC:
				return getArc();
			case LCPnetPackage.CIT__LINES:
				return getLines();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case LCPnetPackage.CIT__NAME:
				setName((String)newValue);
				return;
			case LCPnetPackage.CIT__ARC:
				setArc((CIArc)newValue);
				return;
			case LCPnetPackage.CIT__LINES:
				getLines().clear();
				getLines().addAll((Collection<? extends CITLine>)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case LCPnetPackage.CIT__NAME:
				setName(NAME_EDEFAULT);
				return;
			case LCPnetPackage.CIT__ARC:
				setArc((CIArc)null);
				return;
			case LCPnetPackage.CIT__LINES:
				getLines().clear();
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc --> <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case LCPnetPackage.CIT__NAME:
				return NAME_EDEFAULT == null ? name != null : !NAME_EDEFAULT.equals(name);
			case LCPnetPackage.CIT__ARC:
				return getArc() != null;
			case LCPnetPackage.CIT__LINES:
				return lines != null && !lines.isEmpty();
		}
		return super.eIsSet(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public String toString() {
		if (eIsProxy()) return super.toString();

		StringBuffer result = new StringBuffer(super.toString());
		result.append(" (name: ");
		result.append(name);
		result.append(')');
		return result.toString();
	}

	public void setOutputVariables(FuzzyRuleSet ruleSet) {
		EList<CITLine> lines = this.getLines();

		// For each line of the CIT..
		for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {

			// 1 - create corresponding output variable declaration as a Real
			Variable outputVariable = new Variable("score_line_" + lineNumber);

			// 2 - create corresponding output variable fuzzy definition
			double[] x = new double[] { 0, 1 };
			double[] y = new double[] { 0, 1 };

			MembershipFunction membershipFunction = new MembershipFunctionPieceWiseLinear(
					x, y);
			LinguisticTerm linguisticTerm = new LinguisticTerm("high",
					membershipFunction);

			outputVariable.add(linguisticTerm);

			// 3 - configure defuzzification
			outputVariable
					.setRuleAggregationMethod(new RuleAggregationMethodMax());
			outputVariable.setDefuzzifier(new DefuzzifierCenterOfGravity(
					outputVariable));

			// variable's default value, used if no rule activates defuzzifier
			outputVariable.setDefaultValue(0.0);

			// 4 - add variable to rule set
			ruleSet.getVariables().put("score_line_" + lineNumber,
					outputVariable);
		}
	}

	public void setRules(FuzzyRuleSet ruleSet) throws LCPnetEvaluationException {
		// Defaults methods for connection and implication
		RuleConnectionMethod andMethod = new RuleConnectionMethodAndMin();
		RuleImplicationMethod ruleImplicationMethod = new RuleImplicationMethodMin();

		ruleSet.setRuleImplicationMethod(ruleImplicationMethod);

		EList<CITLine> lines = this.getLines();

		// For each line of the CIT -> 1 fuzzy rule
		for (int lineNumber = 0; lineNumber < lines.size(); lineNumber++) {
			CITLine line = lines.get(lineNumber);

			FuzzyRule fuzzyRule = new FuzzyRule("rule_line_" + lineNumber);

			// Deduct rule's antecedents from selector set values
			EList<LNodeValue> selectorSetValues = line.getSelectorSetValues();

			fuzzyRule.setAntecedents(this.generateRuleAntecedents(
					selectorSetValues, ruleSet, andMethod));

			// Fix rule's consequent
			Variable variable = ruleSet.getVariable("score_line_" + lineNumber);
			if (variable == null)
				throw new LCPnetEvaluationException("Variable score_line_" + lineNumber
						+ " does not exist");
			
			fuzzyRule.addConsequent(variable, "high", false);

			// add rule to node's fuzzy rule set
			ruleSet.add(fuzzyRule);
		}
	}

	/**
	 * Recursively generate a rule's antecedents (IF a = b AND c = d then) from
	 * the following parameters:
	 * 
	 * @param selectorSetValues
	 *            values of the selector set for a particular line.
	 * @param ruleSet
	 *            the rule set to initialize.
	 * @param andMethod
	 *            the 'and method' used between each term.
	 * @return
	 * @throws LCPnetEvaluationException 
	 */
	private FuzzyRuleExpression generateRuleAntecedents(
			EList<LNodeValue> selectorSetValues, FuzzyRuleSet ruleSet,
			RuleConnectionMethod andMethod) throws LCPnetEvaluationException {
		FuzzyRuleExpression fuzzyRuleExpression = new FuzzyRuleExpression();

		if (selectorSetValues.size() > 1) {
			fuzzyRuleExpression.setRuleConnectionMethod(andMethod);

			LNodeValue firstNodeValue = selectorSetValues.get(0);
			BasicEList<LNodeValue> firstNodeValueAsList = new BasicEList<LNodeValue>();
			firstNodeValueAsList.add(firstNodeValue);

			BasicEList<LNodeValue> remainingNodeValues = new BasicEList<LNodeValue>(
					selectorSetValues.subList(1, selectorSetValues.size()));

			// Recurse
			fuzzyRuleExpression.setTerm1(generateRuleAntecedents(
					firstNodeValueAsList, ruleSet, andMethod));
			fuzzyRuleExpression.setTerm2(generateRuleAntecedents(
					remainingNodeValues, ruleSet, andMethod));
		} else {
			LNodeValue nodeValue = selectorSetValues.get(0);
			String varName = nodeValue.getNode().getName();
			String lingTerm = nodeValue.getLinguisticValue().getName();

			Variable variable = ruleSet.getVariable(varName);
			if (variable == null)
				throw new LCPnetEvaluationException("Variable " + varName
						+ " does not exist");

			FuzzyRuleTerm fuzzyRuleTerm = new FuzzyRuleTerm(variable, lingTerm,
					false);
			fuzzyRuleExpression.add(fuzzyRuleTerm);
		}

		return fuzzyRuleExpression;
	}
} // CITImpl
